/*
-----------------------------------------------------------------------------
This source file is part of SapphireEngine

SapphireEngine is a Framework for Turn Based Strategy games.
For more information see http://code.google.com/p/sapphire-engine/

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA, or go to
http://www.gnu.org/copyleft/lesser.txt.

-----------------------------------------------------------------------------
*/
#ifndef _SAPPHIE_ENGINE_APP_H
#define _SAPPHIE_ENGINE_APP_H

#include <Ogre.h>
#include <OIS/OIS.h>
#include "SapphireMemoryManager.h"
#include "SapphirePrerequisites.h"

#include "GUI/LoadingScreenThread.h"


namespace SapphireEngine
{
	class SapphireEngineApp : public Ogre::Singleton<SapphireEngineApp>
	{
	public:
		void go()
		{
			createRoot();
			defineResources();
			//initializeResourceGroups();
			setupRenderSystem();
			createRenderWindow();
			setupInputSystem();
			setupScene();
			setupGUI();
			Screen *loadingScreen = new LoadingScreen("Intro.layout");
			getMemoryManager()->addPointer(loadingScreen,"Loading Screen");				
			getGUIManager()->addScreen("Intro",loadingScreen);
			getGUIManager()->setScreen("Intro");
			createFrameListener();

			//call init thread
			LoadingScreenThread init;
			if (init.create())
				init.start();
			//render
			startRenderLoop();
		}

		~SapphireEngineApp()
		{
			mInputManager->destroyInputObject(mKeyboard);
			OIS::InputManager::destroyInputSystem(mInputManager);
			getMemoryManager()->removePointer(ScreenListener::getSingletonPtr());
			delete ScreenListener::getSingletonPtr();
			getMemoryManager()->removePointer(getCoreManager());
			delete getCoreManager();
			getMemoryManager()->removePointer(getGUIManager());
			delete getGUIManager();
			getMemoryManager()->removePointer(Ogre::Root::getSingletonPtr());
			delete Ogre::Root::getSingletonPtr();
		}
		//Accessors
		static SapphireEngineApp& getSingleton(void);
		static SapphireEngineApp* getSingletonPtr(void);
	private:
		OIS::Keyboard *mKeyboard;
		OIS::Mouse *mMouse;
		OIS::InputManager *mInputManager;
		

		void createRoot()
		{
			getMemoryManager()->addPointer(new Root(),"Root");
		}

		void defineResources()
		{
			Ogre::String secName, typeName, archName;
			ConfigFile cf;
			cf.load("resources.cfg");

			ConfigFile::SectionIterator seci = cf.getSectionIterator();
			while (seci.hasMoreElements())
			{
				secName = seci.peekNextKey();
				ConfigFile::SettingsMultiMap *settings = seci.getNext();
				ConfigFile::SettingsMultiMap::iterator i;
				for (i = settings->begin(); i != settings->end(); ++i)
				{
					typeName = i->first;
					archName = i->second;
					ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);
				}
			}
		}

		void setupRenderSystem()
		{
			if (!Ogre::Root::getSingletonPtr()->restoreConfig() && !Ogre::Root::getSingletonPtr()->showConfigDialog())
				throw Ogre::Exception(52, "User canceled the config dialog!", "SapphireEngineApp::setupRenderSystem()");

			//// Do not add this to the application
			//RenderSystem *rs = gRoot->getRenderSystemByName("Direct3D9 Rendering Subsystem");
			//                                      // or use "OpenGL Rendering Subsystem"
			//gRoot->setRenderSystem(rs);
			//rs->setConfigOption("Full Screen", "No");
			//rs->setConfigOption("Video Mode", "800 x 600 @ 32-bit colour");
		}

		void createRenderWindow()
		{
			Ogre::Root::getSingletonPtr()->initialise(true, "Sapphire Wars");

		}

		void initializeResourceGroups()
		{
			TextureManager::getSingleton().setDefaultNumMipmaps(5);
			ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
		}

		void setupScene()
		{
			SceneManager *mgr = Ogre::Root::getSingletonPtr()->createSceneManager(ST_GENERIC, "Default SceneManager");
			Camera *cam = mgr->createCamera("Camera");
			Viewport *vp = Ogre::Root::getSingletonPtr()->getAutoCreatedWindow()->addViewport(cam);
		}

		void setupInputSystem()
		{
			size_t windowHnd = 0;
			std::stringstream windowHndStr;
			OIS::ParamList pl;
			RenderWindow *win = Ogre::Root::getSingletonPtr()->getAutoCreatedWindow();

			win->getCustomAttribute("WINDOW", &windowHnd);
			windowHndStr << windowHnd;
			pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
			mInputManager = OIS::InputManager::createInputSystem(pl);

			try
			{
				mKeyboard = static_cast<OIS::Keyboard*>(mInputManager->createInputObject(OIS::OISKeyboard, true));
				mMouse = static_cast<OIS::Mouse*>(mInputManager->createInputObject(OIS::OISMouse, true));
				//mJoy = static_cast<OIS::JoyStick*>(mInputManager->createInputObject(OIS::OISJoyStick, false));
			}
			catch (const OIS::Exception &e)
			{
				throw new Ogre::Exception(42, e.eText, "SapphireEngineApp::setupInputSystem");
			}
		}

		void setupCore()
		{
			getMemoryManager()->addPointer(new Core::CoreManager(),"Core Manager");
		}

		void setupGUI()
		{
			getMemoryManager()->addPointer(new GUIManager(),"GUI Manager");
			getGUIManager()->init();
		}

		void createFrameListener()
		{
			
			getMemoryManager()->addPointer(new ScreenListener(mKeyboard,mMouse),"Screen Listener");
			Ogre::Root::getSingletonPtr()->addFrameListener(ScreenListener::getSingletonPtr());
		}

		void startRenderLoop()
		{
			Ogre::Root::getSingletonPtr()->startRendering();

			//// Do not add this to the application
			//while (gRoot->renderOneFrame())
			//{
			//    // Do some things here, like sleep for x milliseconds or perform other actions.
			//}
		}
	};
}
using SapphireEngine::SapphireEngineApp;

template<> SapphireEngineApp* Ogre::Singleton<SapphireEngineApp>::ms_Singleton = 0;
SapphireEngineApp* SapphireEngineApp::getSingletonPtr(void)
{
	return ms_Singleton;
}
SapphireEngineApp& SapphireEngineApp::getSingleton(void)
{  
	assert( ms_Singleton );  return ( *ms_Singleton );  
}

#endif